﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Toolkit.Web.Infastructure;
using Toolkit.Web.ExtensionMethods;
using System.IO;

namespace Toolkit.Web.ViewComponents.DataGrid
{
    public sealed class DataGridBuilder<T> : ViewComponentBuilderBase<DataGrid<T>> where T : class
    {
        public DataGridBuilder(DataGrid<T> component)
            : base(component)
        {
            if (component == null)
            {
                throw new ArgumentNullException("component");
            }
        }

        #region Settings

        public DataGridBuilder<T> HtmlAttributes(object attributes)
        {
            Component.HtmlAttributes.Merge(attributes, true);
            return this;
        }

        public DataGridBuilder<T> Columns(Action<DataGridColumnFactory<T>> configurator)
        {
            if (configurator == null)
            {
                throw new ArgumentNullException("configurator");
            }

            Component.ColumnFactory = configurator;
            return this;
        }

        public DataGridBuilder<T> ColumnHidingAndShowing(bool enable)
        {
            return ColumnHidingAndShowing(enable, null, null, null);
        }

        public DataGridBuilder<T> ColumnHidingAndShowing(bool enable, int minVisible, int maxVisible)
        {
            return ColumnHidingAndShowing(enable, minVisible, maxVisible, null);
        }

        public DataGridBuilder<T> ColumnHidingAndShowing(bool enable, bool renderHidden)
        {
            return ColumnHidingAndShowing(enable, null, null, renderHidden);
        }

        public DataGridBuilder<T> ColumnHidingAndShowing(bool? enable, int? minVisible, int? maxVisible, bool? renderHidden)
        {
            if (enable.HasValue)
            {
                Component.Settings.EnableColumnHidingAndShowing = enable.Value;
            }

            if (minVisible.HasValue)
            {
                if (minVisible.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("minVisible");
                }

                Component.Settings.MinimumNumberOfVisibleColumns = minVisible.Value;
            }

            if (maxVisible.HasValue)
            {
                if (maxVisible.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("maxVisible");
                }

                Component.Settings.MinimumNumberOfVisibleColumns = maxVisible.Value;
            }

            if (renderHidden.HasValue)
            {
                Component.Settings.RenderHiddenColumns = renderHidden.Value;
            }

            return this;
        }

        public DataGridBuilder<T> ColumnResizing(bool enable, int? minWidth, int? maxWidth)
        {
            Component.Settings.EnableColumnResizing = enable;

            if (minWidth.HasValue)
            {
                if (minWidth.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("minWidth");
                }

                Component.Settings.MinimumColumnWidth = minWidth.Value;
            }

            if (maxWidth.HasValue)
            {
                if (maxWidth.Value < 1)
                {
                    throw new ArgumentOutOfRangeException("maxWidth");
                }

                Component.Settings.MaximumColumnWidth = maxWidth.Value;
            }

            return this;
        }

        public DataGridBuilder<T> ColumnReordering(bool enable)
        {
            Component.Settings.EnableColumnReordering = enable;
            return this;
        }

        public DataGridBuilder<T> ColumnWidth(int? defaultWidth, int? minWidth, int? maxWidth)
        {
            if (defaultWidth.HasValue)
            {
                if (defaultWidth.Value < 1)
                {
                    throw new ArgumentOutOfRangeException("defaultWidth");
                }

                Component.Settings.DefaultColumnWidth = defaultWidth.Value;
            }

            if (minWidth.HasValue)
            {
                if (minWidth.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("minWidth");
                }

                Component.Settings.MinimumColumnWidth = minWidth.Value;
            }

            if (maxWidth.HasValue)
            {
                if (maxWidth.Value < 1)
                {
                    throw new ArgumentOutOfRangeException("maxWidth");
                }

                Component.Settings.MaximumColumnWidth = maxWidth.Value;
            }

            return this;
        }

        public DataGridBuilder<T> ColumnSorting(bool enable)
        {
            Component.Settings.EnableColumnSorting = enable;
            return this;
        }

        public DataGridBuilder<T> ColumnSorting(string expression, bool descending)
        {
            if (string.IsNullOrEmpty(expression))
            {
                throw new ArgumentException("expression");
            }

            Component.Settings.EnableColumnSorting = true;
            Component.Settings.SortByColumnExpression = new DataExpression(expression);
            Component.Settings.IsSortedInDescendingOrder = descending;

            return this;
        }

        public DataGridBuilder<T> ViewPort(bool allowResizing, int? maxHeight)
        {
            return ViewPort(allowResizing, maxHeight, null);
        }

        public DataGridBuilder<T> ViewPort(bool? allowResizing, int? maxHeight, int? defaultMaxHeight)
        {
            if (allowResizing.HasValue)
            {
                Component.Settings.EnableViewPortResizing = allowResizing.Value;
            }

            if (maxHeight.HasValue)
            {
                if (maxHeight.Value < 1)
                {
                    throw new ArgumentOutOfRangeException("max");
                }

                Component.Settings.MaximumViewPortHeight = maxHeight.Value;
            }

            if (defaultMaxHeight.HasValue)
            {
                if (defaultMaxHeight.Value < 1)
                {
                    throw new ArgumentOutOfRangeException("defaultMax");
                }

                Component.Settings.DefaultMaximumViewPortHeight = defaultMaxHeight.Value;
            }

            return this;
        }

        public DataGridBuilder<T> RowAction(Action<DataGridRow<T>> action)
        {
            Component.RowAction = action;
            return this;
        }

        public DataGridBuilder<T> RowHeight(int? bodyHeight, int? headerHeight)
        {
            return RowHeight(bodyHeight, headerHeight, null, null, null);
        }

        public DataGridBuilder<T> RowHeight(int? bodyHeight, int? headerHeight, int? filterHeight)
        {
            return RowHeight(bodyHeight, headerHeight, filterHeight, null, null);
        }

        public DataGridBuilder<T> RowHeight(int? bodyHeight, int? headerHeight, int? filterHeight, int? footerHeight, int? defaultHeight)
        {
            if (bodyHeight.HasValue)
            {
                if (bodyHeight.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("bodyHeight");
                }
                Component.Settings.BodyRowHeight = bodyHeight.Value;
            }

            if (headerHeight.HasValue)
            {
                if (headerHeight.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("headerHeight");
                }
                Component.Settings.HeaderRowHeight = headerHeight.Value;
            }

            if (filterHeight.HasValue)
            {
                if (filterHeight.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("filterHeight");
                }
                Component.Settings.FilterRowHeight = filterHeight.Value;
            }

            if (footerHeight.HasValue)
            {
                if (footerHeight.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("footerHeight");
                }
                Component.Settings.FooterRowHeight = footerHeight.Value;
            }

            if (defaultHeight.HasValue)
            {
                if (defaultHeight.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("defaultHeight");
                }
                Component.Settings.DefaultRowHeight = defaultHeight.Value;
            }

            return this;
        }

        public DataGridBuilder<T> ChildRowAction(Action<DataGridChildRow<T>> action)
        {
            Component.ChildRowAction = action;
            return this;
        }

        public DataGridBuilder<T> ChildRowHeight(int? height, int? defaultHeight)
        {
            if (height.HasValue)
            {
                if (height.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("height");
                }

                Component.Settings.ChildRowHeight = height.Value;
            }

            if (defaultHeight.HasValue)
            {
                if (defaultHeight.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("defaultHeight");
                }

                Component.Settings.DefaultChildRowHeight = defaultHeight.Value;
            }

            return this;
        }

        public DataGridBuilder<T> CellAction(Action<DataGridCell<T>> action)
        {
            Component.CellAction = action;
            return this;
        }

        public DataGridBuilder<T> ColumnMenus(bool enable, object attributes)
        {
            Component.Settings.EnableColumnMenus = enable;

            if (attributes != null)
            {
                Component.Menu.HtmlAttributes.Merge(attributes, true);
            }

            return this;
        }

        public DataGridBuilder<T> DataBinding(bool? enableAjax, string httpMethod, string selectUrl)
        {
            if (enableAjax.HasValue)
            {
                Component.Settings.EnableAjaxBinding = enableAjax.Value;
            }

            if (!string.IsNullOrEmpty(httpMethod))
            {
                if (!httpMethod.IsCaseInsensitiveEqual("get") && !httpMethod.IsCaseInsensitiveEqual("post"))
                {
                    throw new ArgumentException("httpMethod");
                }

                Component.Settings.HttpMethod = httpMethod;
            }

            // Note: 'selectUrl' can be empty string.
            if (selectUrl != null)
            {
                Component.Settings.SelectUrl = selectUrl;
            }

            return this;
        }

        public DataGridBuilder<T> Pagination(DataGridPagingType type)
        {
            return Pagination(type, null, null, null);
        }

        public DataGridBuilder<T> Pagination(DataGridPagingType type, int? recordStartIndex, int? recordSetSize, int? totalRecordCount)
        {
            Component.Settings.PagingType = type;

            if (type == DataGridPagingType.None)
            {
                Component.Settings.RecordStartIndex = 0;
                Component.Settings.RecordSetSize = null;
                Component.Settings.TotalRecordCount = null;
            }
            else if (type == DataGridPagingType.BufferedScrolling)
            {
                Component.Settings.RecordStartIndex = 0;
                Component.Settings.RecordSetSize = null;
                Component.Settings.TotalRecordCount = null;
            }
            else if (type == DataGridPagingType.DefaultPaging)
            {
                Component.Settings.RecordStartIndex = recordStartIndex.HasValue ? recordStartIndex.Value : 0;

                if (recordSetSize.HasValue)
                {
                    Component.Settings.RecordSetSize = recordSetSize;
                }

                if (totalRecordCount.HasValue)
                {
                    Component.Settings.TotalRecordCount = totalRecordCount;
                }
            }
            else if (type == DataGridPagingType.BufferedPaging)
            {
                Component.Settings.RecordStartIndex = recordStartIndex.HasValue ? recordStartIndex.Value : 0;

                if (recordSetSize.HasValue)
                {
                    Component.Settings.RecordSetSize = recordSetSize;
                }

                if (totalRecordCount.HasValue)
                {
                    Component.Settings.TotalRecordCount = totalRecordCount;
                }
            }
            else if (type == DataGridPagingType.VirtualPaging)
            {
                Component.Settings.RecordStartIndex = recordStartIndex.HasValue ? recordStartIndex.Value : 0;

                if (recordSetSize.HasValue)
                {
                    Component.Settings.RecordSetSize = recordSetSize;
                }

                if (totalRecordCount.HasValue)
                {
                    Component.Settings.TotalRecordCount = totalRecordCount;
                }
            }

            return this;
        }

        public DataGridBuilder<T> PageSizeOptions(params int[] options)
        {
            Component.Settings.PageSizeOptions = options.ToList();
            return this;
        }

        public DataGridBuilder<T> PersistSettings(bool enable, string url, string key)
        {
            if (enable && string.IsNullOrEmpty(url))
            {
                throw new ArgumentException("url");
            }

            if (enable)
            {
                Component.Settings.EnablePersistence = true;
                Component.Settings.PersistenceUrl = url;
                Component.Settings.PersistenceKey = key;
            }
            else
            {
                Component.Settings.EnablePersistence = false;
                Component.Settings.PersistenceUrl = null;
                Component.Settings.PersistenceKey = null;
            }

            return this;
        }

        public DataGridBuilder<T> ClientEventHandlers(Action<ClientEventHandlers> handlers)
        {
            if (handlers != null)
            {
                handlers(Component.ClientEventHandlers);
            }
            return this;
        }

        #endregion Settings

        #region Templates

        public DataGridBuilder<T> LoadingOverlay(Action<object, IViewComponentWriter> template)
        {
            Component.LoadingOverlayTemplate = new HtmlTemplate()
            {
                ActionTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> LoadingOverlay(Func<object, object> template)
        {
            Component.LoadingOverlayTemplate = new HtmlTemplate()
            {
                InlineTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> NoResultsMarkup(Action<object, IViewComponentWriter> template)
        {
            Component.NoResultsTemplate = new HtmlTemplate()
            {
                ActionTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> NoResultsMarkup(Func<object, object> template)
        {
            Component.NoResultsTemplate = new HtmlTemplate()
            {
                InlineTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> Header(Action<IReadOnlyDataGrid<T>, IViewComponentWriter> template)
        {
            Component.HeaderTemplate = new HtmlTemplate<IReadOnlyDataGrid<T>>()
            {
                ActionTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> Header(Func<IReadOnlyDataGrid<T>, object> template)
        {
            Component.HeaderTemplate = new HtmlTemplate<IReadOnlyDataGrid<T>>()
            {
                InlineTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> Footer(Action<IReadOnlyDataGrid<T>, IViewComponentWriter> template)
        {
            Component.FooterTemplate = new HtmlTemplate<IReadOnlyDataGrid<T>>()
            {
                ActionTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> Footer(Func<IReadOnlyDataGrid<T>, object> template)
        {
            Component.FooterTemplate = new HtmlTemplate<IReadOnlyDataGrid<T>>()
            {
                InlineTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> AddAdditionalHiddenMarkup(Action<object, IViewComponentWriter> template)
        {
            if (template != null)
            {
                Component.AdditionalHiddenTemplates.Add(new HtmlTemplate()
                {
                    ActionTemplate = template
                });
            }

            return this;
        }

        public DataGridBuilder<T> AddAdditionalHiddenMarkup(Func<object, object> template)
        {
            if (template != null)
            {
                Component.AdditionalHiddenTemplates.Add(new HtmlTemplate()
                {
                    InlineTemplate = template
                });
            }

            return this;
        }

        public DataGridBuilder<T> DefaultColumnHeader(Action<object, IViewComponentWriter> template)
        {
            Component.DefaultColumnHeaderTemplate = new HtmlTemplate()
            {
                ActionTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> DefaultColumnHeader(Func<object, object> template)
        {
            Component.DefaultColumnHeaderTemplate = new HtmlTemplate()
            {
                InlineTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> DefaultColumnFooter(Action<object, IViewComponentWriter> template)
        {
            Component.DefaultColumnFooterTemplate = new HtmlTemplate()
            {
                ActionTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> DefaultColumnFooter(Func<object, object> template)
        {
            Component.DefaultColumnFooterTemplate = new HtmlTemplate()
            {
                InlineTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> DefaultColumnBody(Action<T, IViewComponentWriter> template)
        {
            Component.DefaultColumnBodyTemplate = new HtmlTemplate<T>()
            {
                ActionTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> DefaultColumnBody(Func<T, object> template)
        {
            Component.DefaultColumnBodyTemplate = new HtmlTemplate<T>()
            {
                InlineTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> DefaultColumnEditor(Action<T, IViewComponentWriter> template)
        {
            Component.DefaultColumnEditorTemplate = new HtmlTemplate<T>()
            {
                ActionTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> DefaultColumnEditor(Func<T, object> template)
        {
            Component.DefaultColumnEditorTemplate = new HtmlTemplate<T>()
            {
                InlineTemplate = template
            };

            return this;
        }

        public DataGridBuilder<T> AddMenuSection(Action<IReadOnlyDataGrid<T>, IViewComponentWriter> template)
        {
            if (template != null)
            {
                Component.Menu.UniversalSectionTemplates.Add(new HtmlTemplate<IReadOnlyDataGrid<T>>()
                {
                    ActionTemplate = template
                });
            }

            return this;
        }

        public DataGridBuilder<T> AddMenuSection(Func<IReadOnlyDataGrid<T>, object> template)
        {
            if (template != null)
            {
                Component.Menu.UniversalSectionTemplates.Add(new HtmlTemplate<IReadOnlyDataGrid<T>>()
                {
                    InlineTemplate = template
                });
            }

            return this;
        }

        public DataGridBuilder<T> ErrorLoadingDataMarkup(Action<object, IViewComponentWriter> template)
        {
            if (template != null)
            {
                Component.ErrorLoadingDataTemplate = new HtmlTemplate()
                {
                    ActionTemplate = template
                };
            }

            return this;
        }

        public DataGridBuilder<T> ErrorLoadingDataMarkup(Func<object, object> template)
        {
            if (template != null)
            {
                Component.ErrorLoadingDataTemplate = new HtmlTemplate()
                {
                    InlineTemplate = template
                };
            }

            return this;
        }

        #endregion Templates
    }
}
